package com.catoop.spring.prop;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

/**
 * 解密
 *
 * @author 单红宇(CSDN CATOOP)
 * @create 2017年3月20日
 */
public class DecryptPropsBean {

	/**
	 * 用来对加密的内容进行解密的公钥<br/>
	 * 1.可以直接配置公钥字符串；如：props.publickey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJPsIdV==
	 * <br/>
	 * 2.可以配置公钥的本地文件绝对路径；如：props.publickey=file://E:/key/publickey.key或Linux下的/data/key/publickey.key
	 * <br/>
	 * 3.可以配置公钥的网络地址路径；如：props.publickey=http://192.168.x.x:8081/key/publickey.key
	 * <br/>
	 *
	 */
	private String publicKeyName = "props.publickey";
	private List<String> encryptKeyList = new ArrayList<String>();

	// 被加密的值的前缀，例如某个值为 ENCRYPT@@MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJPsIdV==，则根据前缀标记确认该值为加密过的
	private static final String ENCRYPTVALUE_PREFIX = "ENCRYPT@@";
	
	/**
	 * 解密属性值
	 * 
	 * @param props
	 * @throws Exception
	 */
	@SuppressWarnings("rawtypes")
	public void decryptProps(Properties props) throws Exception {
		if(props != null)
			decryptMap((Map)props);
	}
	
	/**
	 * 解密属性值
	 * 
	 * @param props
	 * @throws Exception
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void decryptMap(Map map) throws Exception {
		String publicKey = getPublicKey(map);
		
		if (publicKey != null && publicKey.length() > 0) {
			String publicKeyText;
			// 判断publickey为Text，还是路径
			if (publicKey.startsWith("file://") || publicKey.startsWith("http") || publicKey.startsWith("/")) {
				publicKeyText = loadPublicKey(publicKey);
			} else {
				publicKeyText = publicKey;
			}
			if(encryptKeyList.isEmpty()){
				// 读取配置重的keyList
				encryptKeyList = getEncryptKeyList(map);
			}
			for (String encryptKey : encryptKeyList) {
				if (map.containsKey(encryptKey)) {
					Object encryptValue = map.get(encryptKey);
					if (encryptValue != null && encryptValue.toString().length() > 0) {
						// 对密文进行解密
						String decryptValue = EncryptTools.decrypt(publicKeyText.trim(), encryptValue.toString().trim());
						map.put(encryptKey, decryptValue);
					}
				}
			}
		}
	}
	
	/**
	 * 从配置文件中读取公钥
	 *
	 * @param props
	 * @return
	 * 
	 * @author 单红宇(CSDN CATOOP)
	 * @create 2017年3月11日
	 */
	@SuppressWarnings("rawtypes")
	private String getPublicKey(Map map) {
		Object o = map.get(publicKeyName);
		if(o != null)
			return o.toString();
		return null;
	}
	
	/**
	 * 从配置文件中读取加密的key列表
	 *
	 * @param map
	 * @return
	 * 
	 * @author 单红宇(CSDN CATOOP)
	 * @create 2017年4月21日
	 */
	private List<String> getEncryptKeyList(Map<Object, Object> map) {
		for (Entry<Object, Object> entry : map.entrySet()) {
			if(entry != null){
				Object o = entry.getValue();
				if(o != null && entry.getKey() != null && o.toString().startsWith(ENCRYPTVALUE_PREFIX)){
					encryptKeyList.add(entry.getKey().toString());
					map.put(entry.getKey(), entry.getValue().toString().replaceFirst(ENCRYPTVALUE_PREFIX, ""));
				}
			}
		}
		return encryptKeyList;
	}

	/**
	 * 加载配置文件 配置文件支持3中路径格式： <br/>
	 * 1.file://D:/resource/publickey.txt <br/>
	 * 2.http://www.xxx.com/res/publickey.txt <br/>
	 * 3./usr/local/res/publickey.txt <br/>
	 * 
	 * @param filePath
	 * @return
	 * @throws Exception
	 */
	public String loadPublicKey(String filePath) throws Exception {
		InputStream inStream = null;
		BufferedReader reader = null;
		try {
			if (filePath.startsWith("file://")) {
				filePath = filePath.substring("file://".length());
				inStream = getFileAsStream(filePath);
			} else if (filePath.startsWith("http://") || filePath.startsWith("https://")) {
				URL url = new URL(filePath);
				inStream = url.openStream();
			} else {
				inStream = getFileAsStream(filePath);
			}

			if (inStream == null) {
				throw new Exception("load publicKey file error, file : " + filePath);
			}

			reader = new BufferedReader(new InputStreamReader(inStream, "UTF-8"));
			StringBuilder sb = new StringBuilder();
			String line = null;
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
			return sb.toString().trim();
		} finally {
			EncryptTools.close(reader);
			EncryptTools.close(inStream);
		}
	}

	private InputStream getFileAsStream(String filePath) throws FileNotFoundException {
		InputStream inStream = null;
		File file = new File(filePath);
		if (file.exists()) {
			inStream = new FileInputStream(file);
		} else {
			inStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
		}
		return inStream;
	}

	public List<String> getEncryptKeyList() {
		return encryptKeyList;
	}

	/**
	 * 被加密的key
	 *
	 * @param encryptKeyList
	 * 
	 */
	public void setEncryptKeyList(List<String> encryptKeyList) {
		this.encryptKeyList = encryptKeyList;
	}

	public String getPublicKeyName() {
		return publicKeyName;
	}

	public void setPublicKeyName(String publicKeyName) {
		this.publicKeyName = publicKeyName;
	}

}
